﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using dis.sys.hw1.ClientProxy;
using dis.sys.hw1.Model;

namespace dis.sys.hw1.Client
{

    class Program
    {
        private const bool Debug = true;
         // proxy to communicate with searxh service
        private static HotelSearchProxy _service = null;
        private const int SearchServiceAddressArg = 0;

        static void Main(string[] args)
        {
            if (!args.Any())
            {
                PrintErrorMessage("Invalid usage, please use: client.exe <hotel search server URI #1>");
                Console.ReadLine();
                return;
            }

            // initialize proxy
            _service = new HotelSearchProxy((args[SearchServiceAddressArg].StartsWith("http://") ? "" : "http://") + args[SearchServiceAddressArg]);

            if (!(_service != null && _service.IsUp()))
            {
                PrintErrorMessage("Search service is down. press any key to exit.");
                Console.ReadLine();
                return;
            }

            PrintWelcomeMessage();

            while (true)
            {
                PrintPrompt();
                var items = getCommand();
                if (items == null)
                    continue;
                   

                switch (items[0])
                {
                    case "search":
                        DoSearch(items);
                        break;
                    case "book":
                        DoBook(items);
                        break;
                    case "cancel":
                        DoCancel(items);
                        break;
                    case "help":
                        PrintInfoMessage("The following commands are available:");
                        PrintInfoMessage("\t- search <fromDate> <toDate> <minStars> <minRating>");
                        PrintInfoMessage("\t- book   <fromDate> <toDate> <roomNumber> <hotelName>");
                        PrintInfoMessage("\t- cancel <bookingId> <hotelName>");
                        PrintInfoMessage("\t- exit");
                        break;
                    case "exit":
                        return;
                    case "":
                    case null:
                        break;
                    default:
                        PrintErrorMessage("Unsupported command please use 'help'");
                        break;
                }
            }
        }

        // get a command from the user
        // and split to tokens
        private static string[] getCommand()
        {
            var cmd = Console.ReadLine();

            if (cmd == null)
            {
                PrintErrorMessage("Invalid Command");
                return null;
            }

            var items = cmd.Split(' ');
            return items;
        }

        private static void PrintPrompt()
        {
            Console.ForegroundColor = ConsoleColor.DarkCyan;
            Console.Write("> ");
            Console.ResetColor();
        }

        private static void PrintInfoMessage(string msg)
        {
            Console.ForegroundColor = ConsoleColor.Cyan;
            Console.WriteLine(" " + msg);
            Console.ResetColor();
        }

        private static void PrintErrorMessage(string msg)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(" " + msg);
            Console.ResetColor();
        }

        private static void PrintResultMessage(string msg)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(" " + msg);
            Console.ResetColor();
        }

        private static void PrintWelcomeMessage()
        {
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.Write(" ");
            for (int i = 1; i < 49; i++)
            {
                Console.Write("-");
            }
            Console.Write("\n");
            for (int i = 0; i < 2; i++)
            {
                Console.Write("|");
                for (int j = 1; j < 49; j++)
                {
                    Console.Write(" ");
                }
                Console.Write("|\n");
            }

            Console.Write("|    ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write("Welcome to the hotel booking service :)");
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.Write("     |\n");

            Console.Write("|    ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write("     by Yogev Shifman & David Begun    ");
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.Write("     |\n");
            Console.Write("|    ");
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write("         write 'help' for instructions ");
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.Write("     |\n");

            for (int i = 0; i < 2; i++)
            {
                Console.Write("|");
                for (int j = 1; j < 49; j++)
                {
                    Console.Write(" ");
                }
                Console.Write("|\n");
            }
            Console.Write(" ");
            for (int i = 1; i < 49; i++)
            {
                Console.Write("-");
            }
            Console.Write("\n");
            Console.ResetColor();
        }

        private static void DoCancel(string[] items)
        {
            //if (items.Length <= 2)
            //{
            //    PrintErrorMessage("Invalid usage, please use: cancel <bookingId> <hotelName>");
            //    return;
            //}
            //var bookingId = int.Parse(items[1]);
            //var hotelName = string.Join(" ", items.Skip(2));

            //try
            //{
            //    if(_service.Cancel(bookingId, hotelName))
            //    {
            //        PrintResultMessage("OK");
            //    }
            //    else
            //    {
            //        PrintErrorMessage("Failed, no such booking ID");                    
            //    }
            //}
            //catch (Exception exception)
            //{
            //    CheckWebException(exception);
            //}
        }

        private static void DoBook(string[] items)
        {
            //if (items.Length <= 4)
            //{
            //    PrintErrorMessage("Invalid usage, please use: book   <fromDate> <toDate> <roomNumber> <hotelName>");
            //    return;
            //}
            //var fromDate = new DateTime(); 
            //var toDate = new DateTime(); 
            //var roomNumber = int.Parse(items[3]);
            //var hotelName = string.Join(" ", items.Skip(4));

            //if (!CheckTime(ref fromDate,ref toDate, items))
            //    return;

            //try
            //{
            //    int result = _service.Book(fromDate.Ticks, toDate.Ticks, roomNumber, hotelName);
            //    PrintResultMessage("OK, booking ID: " + result);
            //}
            //catch (Exception exception)
            //{
            //    CheckWebException(exception);
            //}
        }

        // extract inner message from exception that came from the web
        private static void CheckWebException(Exception exception)
        {
            var innerException = exception.InnerException as WebException;
            if (innerException != null)
            {
                var resp = (HttpWebResponse)innerException.Response;
                PrintErrorMessage(resp.StatusDescription);
            }
            else if (exception is WebException)
            {
                var resp = (HttpWebResponse)(exception as WebException).Response;
                PrintErrorMessage(resp.StatusDescription);
            }
            else
            {
                PrintErrorMessage(exception.Message);
            }
        }

        /* 
         * parse and check the time given
         * return the values in the fromDate and toDate parameters
         */
        private static bool CheckTime(ref DateTime fromDate,ref DateTime toDate, string[] items)
        {
            try
            {
                fromDate = DateTime.Parse(items[1]);
                toDate = DateTime.Parse(items[2]);
            }
            catch (FormatException)
            {
                PrintErrorMessage("Bad date format. should be 'dd/mm/yy");
                return false;
            }
            return true;
        }

        private static void DoSearch(string[] items)
        {
            if (items.Length != 5)
            {
                PrintErrorMessage("Invalid usage, please use: search <fromDate> <toDate> <minStars> <minRating>");
                return;
            }
            DateTime fromDate = new DateTime(), toDate = new DateTime();
            if (!CheckTime(ref fromDate,ref toDate, items))
                return;
            var minStars = double.Parse(items[3]);
            var minRating = double.Parse(items[4]);

            var daysNum = (int)toDate.Subtract(fromDate).TotalDays;

            try
            {
                var availableHotels = _service.Search(fromDate.Ticks, toDate.Ticks, minStars, minRating);
                PrintHotelsResults(availableHotels, daysNum);
            }
            catch (Exception exception)
            {
                CheckWebException(exception);
            }

            Console.WriteLine();            
        }

        private static void PrintHotelsResults(IList<Hotel> availableHotels, int days)
        {
            if(!availableHotels.Any())
            {
                PrintErrorMessage("Sorry, we found no available rooms, please try different parameters");
            }

            availableHotels = availableHotels.OrderBy(x => x.Name.ToLower()).ToList();

            foreach (Hotel h in availableHotels)
            {
                PrintResultMessage(string.Format("{0}, {1:0.00}/5 stars, rating: {2:0.00}/10", h.Name, h.Stars, h.Rating));
                var sortedRooms = h.Rooms.OrderBy(x => x.Price).ToList();
                foreach (HotelRoom r in sortedRooms)
                {
                    PrintResultMessage("\tRoom: " + r.RoomNumber + " for " + (r.Price * days) +"$");
                }
            }
        }
    }
}
